THE ASSEMBLY LANGUAGE "MAGAZINE" VOL 1 NUMBER 2 March 1989 ## #### #### ####### ## ## ###### #### ## ## #### ## ## ## ## ## # ### ### ## ## ## ## ## ## ## ### ### ## # ####### ## ## ## ## ## ## ## ### ### #### ####### ##### ## #### ###### ### ### ## # ## # ## ## ## ## # ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## #### #### ####### ## ## ###### ####### #### #### ## ## ## #### ## ## ## #### ####### ## #### ### ## ## ## ## ## #### ## ## ## # ## ## ## #### ## ## ## ## ## ## ## ## # ## ## ## ## #### ## ## ## ## ## ## #### ## # ###### ## ### ## ### ## ## ###### ## ### ## # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # ####### ## ## ## ## ##### ###### ## ## ##### ####### Written by and for assembly language programmers. March 1989 Contents Policy................................................... 3 Editorial................................................ 4 Beginners Corner......................................... 5 What is SDN?............................................. 7 FASTPRINT................................................ 8 by Dennis Yelle The EXEC Function........................................ 11 by Patrick O'Riva TSR's.................................................... 13 by David O'Riva Book Reviews............................................. 17 Program Spotlight........................................ 18 Source listings in order of appearance It has been suggested that the source listings be placed at the end of the "Magazine" rather than following the associated article. If you prefer it one way or the other please advise the editor. Address of The Assembly Language "Magazine" AsmLang and CFS FidoNet 143/37 408-259-2223 Patrick O'Riva 2726 Hostetter Rd. San Jose, CA 95132 2 GUIDE LINES FOR CONTRIBUTORS AND 'ADVERTISERS' Name and address must be included with all articles and files. Executable file size and percent of assembly code (when available) should be included when a program is mentioned and is required from an author or publisher. Any article of interest to Assembly language programmers will be considered for inclusion. Quality of writing will not be a factor, but I reserve the right to try and correct spelling errors and minor mistakes in grammar. Non-exclusive copyright must be given. No monetary compensation will be made. Outlines of projects that might be undertaken jointly are welcome. For example: One person who is capable with hardware needs support from a user friendly programmer and a math whiz. Advertisements as such are not acceptable. Authors and publishers wishing to contribute reviews of their own products will be considered and included as space and time permit. These must include executable file size, percent of assembly code and time comparisons. Your editor would like information on math libraries, and reviews of such. Articles must be submitted in pclone readable format or sent E-mail. Money: Your editor has none. Therefore no compensation can be made for articles included. Subscription fees obviously don't exist. Publication costs I expect to be nil (NUL). Small contributions will be accepted to support the BBS where back issues are available as well as files and programs mentioned in articles(if PD or Shareware ONLY). Shareware-- Many of the programs mentioned in the "Magazine" are Shareware. Most of the readers are prospective authors of programs that can be successfully marketed as Shareware. If you make significant use of these programs the author is entitled to his registration fee or donation. Please help Shareware to continue to be a viable marketing method for all of us by urging everyone to register and by helping to distribute quality programs. 3 Editorial This is the second monthly edition of the Assembly Language Magazine. I have been disappointed in the response to the first issue both in the number of comments I have received and in the lack of articles submitted for inclusion in this issue. I would like to give a special thank you to Richard Hendricks for his letter of suggestions and comments, most of which I hope to address in this issue. He felt that a less full look to the pages would improve the readability and appearance. My original idea was to fill the page as much as possible to reduce the amount of print out. Many of his other comments reflect my lack of experience as an editor, and I will try to improve. Without some articles being sent in this will be near the last issue. The time is not available for your editors to write the entire magazine every month, and even if it were, the quality would suffer from lack of variety. Please send those submissions. Please excuse the late publication this month. ERRATA The name of the Author of the A86 Assembler was misspelled. The correct spelling is: Eric Isaacson In the article about fast memory moves I repeatedly referred to the 8259 as the DMA processor. The 8259 is the interrupt controller and has nothing to do with DMA transfers. 4 Beginners Corner. Obviously the first requirement for learning any assembly language is to have a list of the instructions. This may sound easy and it is something that most novice level books claim to have, but it is one of the most difficult things to find. Every source I have found is very incomplete and/or unclear. The exact use of each command and the hundreds of variations on each forces even scarred veterans to look back at a reference now and again. At least one of your references must contain the clocks data for each instruction. For the 80XXX series of processors this is a very confusing table of adds for all occasions. The basic time for the instruction is given plus the number of accesses plus EA calculation. It is different for a conditional jump taken and when it is not. To make matters even worse we are dealing with not one processor, but with at least 5: 8088, 80286, 80386, V20, V30. Each of these works differently and thus each has its own timing. Because it is the worst case and because there are so many installed it is always best to use the timing for the 8088 unless you are writing for a particular application or system. A first glance at the EA calculation table will be very enlightening to those who have used a high level language. Most of the variables passed and most all of the structures addressed are done with pointers and offsets to pointers. These are VERY costly time wise. Even pushes and pops take far longer than one would think. A memory access (fetching the value of a variable) especially using the AX register pair is faster than a push and pop. Be familiar with your instructions and how long they take. Only use half of the register pair if that is all you need because it saves an additional fetch, cutting the time almost in half. A clear verbal description of each instructions including its peculiarities is essential. This is true for all, but the more complex instructions such as rep movsb make it mandatory. Each instruction should list the flags it can affect. These are not always what you want or need. For example: the INC instruction does not affect the carry flag. As silly as it may sound you have to use an instruction that affects the flag you want to test for. The Rotate and Shift group of instructions should be accompanied with diagrams showing their operation. A glance at the diagrams will show exactly which one to use where it could take reading pages to do the same thing. 5 One final note of caution that seems to fit in here -- unless you are doing signed arithmetic operations avoid the use of the JG and JL type instructions. They take the sign bit into account and can cause very hard to trace bugs. Use the JA and JB instructions instead. Your next most important reference is a description of the BIOS calls. Most of these are very straight forward and require little by way of explanation. Next comes a DOS reference. The critical ones of these are also quite simple to understand and use. Don't get involved with FCB's; the handle method of file access is the only way to go. For your BIOS and DOS references almost any will work, but your basic instruction description look long and hard. You will probably end up with several you use regularly. With all of your reference works stacked up around you, sitting at your computer, what next? You need to choose an assembler. There are at least 4 major packages available, each with its supporters and its weaknesses. The Microsoft MASM is complete and the de facto standard. It is also large and slow. TASM from Borland is faster, and is mostly compatible, but tricky for beginners. Optasm I am not very familiar with, though it sounds pretty good. It is much faster than MASM, and is a full 2 pass assembler. If you are not concerned with compatibility A86 is fast, small and simple to use, and the D86 debugger is adequate. The text editor you use must produce a pure ASCII file. Whatever you enjoy is the one to use. Qedit from Semware can be configured to act like almost any other, and is fast and small. Next month: Segmentation and Memory allocation. Useful Shareware: 1029ref DOS and Pc Tech Reference inter Complete interrupt listing @last200 Pop up tables LW86 Pop up instruction summary Qedit Text editor A86 Assembler D86 Symbolic Debugger 6 What is SDN? SDN stands for Shareware Distribution Network. It is a relatively new organization operating largely within FidoNet but in no way affiliated with it. SDN receives software direct from the author on disk, compresses it using the PAK facility from No Gate and distributes it to affiliated BBS's throughout the country, in a highly traceable manner. This distribution method is advantageous for both the author and the user. It assures the author of quick and complete distribution nationwide, and it assure the user of complete and uncontaminated software. There is no charge to either the author or the user for this service, but the author must comply with the instructions for transmitting the software to the SDN home point. Complete information is available from your local SDN BBS or as a last resort you can get the informational files from AsmLang. 7 FASTPRINT: The fix for one of PRINT's bugs by Dennis Yelle Does your printer slow down when you run LIST or your favorite editor, word processor, debugger, or other program that spends most of its time just waiting for you to type at it? Does this annoy you? It annoyed me! It annoyed me enough so that I did something about it. How to use it: -------------- Just type FASTPRINT at the DOS prompt, either before or after you run PRINT. Or put the FASTPRINT command in your AUTOEXEC.BAT file, that's what I do. If you put FASTPRINT in your AUTOEXEC.BAT file, it should normally be placed AFTER any utility that increases the size of the keyboard type-ahead buffer. How it works, if you like grubby details: ----------------------------------------- The PRINT command installs a TSR that prints files in the background. This allows you to run other programs on your computer while the file(s) print. The PRINT TSR "steals" some computer time from you, but usually not enough to bother you. The problem is that sometimes (usually) it doesn't steal enough to keep the printer running at anywhere near full speed. Fortunately, there is a way to "give" the PRINT TSR more time. And, it turns out that COMMAND.COM does just that when it is waiting for you to type a DOS command. The problem is that most programs that you run don't do this. And so the printer slows down. ugh! Now we get to the good part. FASTPRINT looks for some signs that indicate that the foreground program is waiting for something, like a keystroke, and tells PRINT to do some printing while we wait. How it works, only for those who like the REALLY grubby details: ------------------------------------------------------- The way to "give" the PRINT TSR some computer time is to put 0080H in AX, and do an INT 2FH. What FASTPRINT does, is link itself into interrupts 15H and 16H. INT 16H is used to read the keyboard. When a program tries to read a key that the human has not typed yet, FASTPRINT gives PRINT a bit of computer time, and then checks to see if the human has typed the key yet. If not, then it gives PRINT some more time and checks the keyboard again...INT 15H function 90H is used on the AT only to tell other programs that we are waiting for something that may take some time. So, if this happens, then FASTPRINT gives some time to PRINT. 8 To make FASTPRINT even more useful to those who like grubby details, I am including the A86 source code for the program. How to pay for it: ------------------ 1. If it is before March 30, 1989 then just: Send a message to me reporting how well FASTPRINT worked for you, including: (A. the hardware you are running on; (B. the version of DOS you are using; (C. how well it works, that is, for example, how many seconds does it take to print a www byte xxx line file with a yyy printer with and without using FASTPRINT while inside program zzz; and (D. anything else you have discovered about it, including any incompatibility with other programs. You may send this message to my PO Box, or to me at any of these BBS'S: AsmLang and CFS (Opus 1:143/37) 408-259-2223 HomeBase 408-988-4004 PDSE 408-745-0880 2. Just send a check for about 1/4 of what you think it is worth to you to: Dennis Yelle P. O. Box 62276 Sunnyvale, CA 94088 If you want to donate, but don't know how much it is worth, then just send me $10. 3. FOR PROGRAMMERS ONLY: If you want to use any or all of the source code in the file FASTPRIN.8 in your own program(s), you may buy the right to do so for $25. Legal mumbo jumbo ----------------- This document and the program files FASTPRIN.COM and FASTPRIN.8 are copyrighted by the author. The copyright owner hereby licenses you to: use the software; make as many copies of it as you wish; give the copies to anyone; and to post this .ARC file on BBS'S, if the BBS is free. There is no charge for any of the above. However, you are specifically prohibited from charging, or requesting donations, for any copies, however made; and from distributing this software and/or documentation with commercial products without prior permission. No copy of this software may be distributed or given away without this document; and this notice must not be removed. There is no warranty of any kind, and the copyright owner is not liable for damages of any kind. By using this software, you agree to this. 9 -------------------------------------------------------------- Editors note: This is the documentation for FASTPRINT and has been included in the magazine at the author's suggestion as it describes the operation, is written in assembly and includes the source. 10 The EXEC Function by Patrick O'Riva The EXEC function is one of the most powerful features of DOS. It allows shelling out of an application, and even going as far as invoking another command interpreter. It is also rather complex to use and there is no debugging help from DOS. The EXEC command comes in three basic flavors; Load and execute a file, Load and overlay, and the 3rd is an internal DOS function of load a file. Only the load and execute a file will be covered in detail this month. I freely admit I'm not an expert on the use of the EXEC function, but I have convinced it to work for me and done so recently enough I can remember most of the mistakes. If anyone reading this is better informed, I encourage them to write a supplementary article. DOS requires that you supply three separate pieces of information. The program fully qualified as to drive, path, filename and extension. My experience is that it will not make a path search, but this might be inaccurate and caused by a slight error in syntax. The second item is the command line arguments in DOS format, where the first byte specifies the length of the argument including the length byte. The third item is an environment block. For this one you are allowed to default to current block as passed to the program you are EXEC'ing out of. To execute a batch file or use the built-in DOS commands you must EXEC command.com itself and pass the program name in the arguments. For "load and execute" a program the syntax is as follows: AH=4Bh EXEC function number within INT 21h AL=0 specify "load and execute" DX points to program name, i.e. 'C:\MYSTUFF\RUNME.EXE' ES:BX points to the "EXEC control block", defined as follows: WORD segment of environment block to pass if 0 defaults to parent block. environment is assumed to be at offset 0 within this segment DWORD pointer to command tail DWORD pointer to 1st FCB DWORD pointer to 2nd FCB "COMMAND TAIL" is defined as that portion of a command line following the program name, preceded by a one byte count 11 of the length of the tail plus 1 for the terminating CR (0Dh), i.e. DB 12,'/h /Ox1.obj',0Dh The first job most any program should do is return to DOS any portion of memory that is not required. Without this operation there would be no place for DOS to load the new program. It is also best to do this to inform your program how much memory is available to it to use for data storage and other general uses. If FFFF paragraphs are requested from DOS naturally a fail will be returned, but more importantly the amount available will also be returned. After determining that there is sufficient to at least attempt the EXEC the program can continue. Let's use the terms parent and child for the two programs. Once the EXEC is called the parent has no further control over the operation of the child. All control must be exercised through the information passed in the parameter block. You can tailor the environment block, specify the command tail, and point the 2 FCB's if the program uses them. If the program doesn't use them you can just point them to 5CH and 6CH of the parent's PSP(The segment address is supplied by DOS in ES and DS after the parent is loaded). I am including a short program that does nothing except EXEC according to your instructions. With some playing around it should be a good introductory tool. A sample program name and command tail are included, but these you will want to change. Some of the code in there is unnecessary, but may make it easier to customize or illustrates some point. With relatively small changes this could be included in another program to serve as an exec subroutine. The source is compatible with both MASM and A86. 12 TSR's How to get user input, Part Two (along with lotsa other neat stuff) by David O'Riva This article is a multi-part package, all rolled into one month, mainly because a good deal of it comes in one chunk: How to write a well-behaved TSR. For those of you who aren't quite sure what a TSR really is, a short definition follows. TSR, n, 1. Terminate and Stay Resident. 2. A program or routine that, after being loaded, carves a niche of memory for itself and holes up there, only showing itself when a certain circumstance occurs. This is what is meant by"serially multitasking." You press a "hotkey", and for some length of time the program you were working on is subjugated to the background, and the TSR gets to use the computer. 3. A real pain to write. TSR'S have numerous uses: print spoolers, expanded keyboard buffers, text editors, directory manipulators, background disk formatters, display managers, background file transferors - anything you may want to do while you're in the middle of another application. TSR's usually have the following basic structure (depending on what they do): jump to initialization main body of resident code keyboard interrupt interceptor timer tick interceptor DOS console interceptor un-installation routine initialization code An explanation of each of these follows: Initialization Code The initialization code must check to see if the TSR is already resident in memory (aborting if so), hook all of the interrupt servers into their respective chains, perform any other tasks necessary to install the program, then deallocate all unused memory and exit to DOS with a TSR call. 13 To determine whether or not a copy of the TSR is already resident, you could assign an unused interrupt to it. The example uses INT 69H, function ABCD hex. If the example is already installed, it returns with DCBA hex in AX. This "residency check" interrupt could also handle functions for the TSR: i.e. if the TSR was a print spooler,and there was no "hot key" code written for it (like the DOS PRINT command), then the program could call INT 69H with function AB00H to add a file to the print queue. To hook an interrupt into a chain, you must replace the appropriate vector with a pointer to your interrupt server, and your interrupt server must jump to or call the code that the vector used to point to. Failure to do this caused many early TSR's to refuse to co-habit with other TSR's, occasionally making the machine bomb, etc... Unless you have a very good reason, ALWAYS continue the interrupt chain. Two TSR exits are available: The first is INT 27H,which has some limitations (only 64K of your program can remain resident, which shouldn't be too onerous), and which Microsoft does not recommend for anything except DOS 1. The second is INT 21H function 31H, which allows up to a meg to remain resident (right) and allows you to send a return code back to the caller. The initialization code usually de-allocates itself upon exiting. Keyboard Interrupt Interceptor This is only required for programs that wish to pop up when the hot key is pressed or wish to monitor the keyboard for some other reason. Much of what they have to do was discussed last month, with two exceptions. The first is how to prevent a keystroke from reaching the BIOS code without leaving the keyboard locked up. To do this, you have to tell the keyboard that you received the character, then reset the interrupt controller and exit (without chaining to the next interrupt.) The keyboard is cleared by toggling bit 7 of port 61H on, then off. The interrupt controller is cleared by sending an EOI(End Of Interrupt) code 20H to the 8259 at port 20H (weird,huh?). At that point you should do an IRET. The second is the necessity of monitoring the InDOS flag. Note that YOU ONLY HAVE TO DO THIS IF YOUR TSR USES DOS FUNCTIONS. Also note that this flag is NOT documented by IBM OR Microsoft, and that it is not strictly "compatible." 14 The address of the InDOS flag can be acquired by a call to INT 21H function 34H. The flag is non-zero if a DOS function is currently active. You need this flag because DOS is not re-entrant. That is, if you call DOS from your TSR,and DOS was active when the TSR gained control, the TSR will work fine, but when you return from it, the function in progress will have lost its entire stack and much of its data area. Nasty stuff. If the InDOS flag is set when the keyboard trap is activated, the keyboard trap should set a flag saying "I want to activate but I can't" and return. Timer Tick Interceptor These are always fun. 18.2 times per second, you get control of the machine. This is useful for print spoolers, resident screen clocks, and TSR'S that need to perform DOS functions. In the last case, the timer tick trap should check to see if the TSR wants to activate, and if so whether the InDOS flag is set. If the InDOS flag is clear, then the TSR can be activated. DOS Console Interceptor This interrupt (INT 28H) is called by DOS whenever a DOS console function is in progress. This is useful to have because during a console input, the InDOS flag is set, but it is actually safe to use any DOS function ABOVE 0CH. This trap should check to see if the TSR wants to activate, and if so then activate it. Main Body of the TSR The only requirement for this is that if you use more than two words (yes, this is an arbitrary measure that seems safe to me) of stack, you should use your internal stack instead of the one that you were called with. This is because you have no idea how large the stack you're working on is, and no way of telling how far you can go before you start to overwrite data... The Un-Installation routine This is a very nice thing to have. The only problem is that it needs to use a DOS call to de-allocate its memory. 15 Unless, of course, you want to directly modify the memory control blocks... which could be very dangerous. The routine in the example TSR does check the memory control block immediately after it to make sure that there are no programs installed in memory after the TSR. DOS gets upset when there are "holes" in memory. At least, it's supposed to. The structure included for the MCB is complete as far as I know, but it is also *extremely* undocumented. Microsoft calls it an "arena header" and tells programmers to keep their hands off of it. All vectors must be unhooked. The DOS calls to de-allocate the memory are necessary. Neat tricks you can pull... The command line area in the PSP makes for a convenient 40 word internal stack. If you aren't using the FCBS, the FCB storage area can be commandeered as well. The environment segment's size can be checked (with a look at the MCB) and that can be used for stack, data or code as well. The Example TSR As far as actual use goes, the example TSR is a very simple beast. When resident, press LSHIFT-RSHIFT-T (it'll beep). Then press "Q" to return to normal processing, or press "N" to Nuke the TSR (remove it from memory). If it cannot remove itself, it will beep three times. Well, that's about all there is to it. With all these routines (or at least the ones you need) combined in one cohesive package, you have a full-fledged TSR skeleton. The example TSR includes most of these techniques. Use and enjoy. Comments on this article, or expansions on the subject matter are greatly appreciated and will be published in later issues of the magazine. 16 Book Reviews by Patrick O'Riva PROGRAMMER'S TECHNICAL REFERENCE FOR MSDOS AND THE IBM PC By Dave Williams P.O. box 181, Jacksonville, AR 72087 This is a first glance review that I wanted to get into this month's issue. It is a user supported book on disk. I haven't had the time to read through it, but it appears to be one of the best available in any format. In compressed form it is over 200k, and expands to over 500k. It is very complete and includes some hard to find tables and version histories. Requested $15 support includes 2 additional disks and updates. Probably one of the best buys around. It is available on various BBS's (AsmLang included) though its 200k+ size makes for slow distribution. The complete package direct from the author is probably best. IBM Technical Reference- Personal Computer XT This comes in the standard binder/box and is available from IBM corporation at a hefty $50. In addition to some information on port assignments and memory maps it contains two sets of information that I have found nowhere else. A complete listing of the ROM BIOS for the XT that is invaluable as both an example of programming each piece of hardware and clues as to how to optimize for a specific application. Complete schematics of the motherboard of the XT that can help to explain why something won't work if you are technically inclined. I have found it invaluable, but it is not for everyone. Similar publications are available for the other IBM products and for the PCDOS. For further information contact IBM at 1-800-426-7282 17 Program Spotlight Exceptional Programs This is really a nonexistent column this month as nothing new has come to my notice that meets the qualifications of high speed and small size. There is a nice piece of source code available under the name of CRC16 that calculates and verifies 16 bit CRC values. If this meets a need of yours keep a look out for it. Although I haven't tried it out yet, a program called the Brand X symbolic debugger has been highly recommended to me. Other comments would be welcomed. QFILE31G handles copying, deleting and moving files, as well as maintaining and listing ARC's and ZIP's. Nicely done, but too slow and too large to really fit here, and hangs up on some error conditions. WHIZ1 a file finding program certainly qualifies in speed as a multi disk search with wild card only takes a few seconds. It is a bit larger than it might be, but at the least it is enhanced with assembly routines. It is a shareware offering and is widely available. 18 ;Ed -This is the source code on FASTPRINT jmp install ; This will be a .COM db cr, ' ', cr, lf ; On most displays, if this file is TYPEed, ; these 3 spaces will erase ; the 3 characters in the JMP instruction above. message0: db 'FASTPRIN 0.0 Copyright 1989 by Dennis Yelle,' db ' PO Box 62276, Sunnyvale CA 94088' db cr, lf message0_len = $ - message0 db 'Last change: Mar 4, 1989', cr, lf db 26 ; This 26 is a control-Z, the DOS EOF character. ; I put it here so that if someone TYPEs this .COM file, ; only the characters before the 26 will be printed on the screen. id_size = $ - 0100 ; To assemble this program, put the source in a file called ; FASTPRIN.8 and then type ; A86 FASTPRIN.8 ; This will produce a file called FASTPRIN.COM directly, ; in about 2 seconds! No need to run a linker! ; The program A86.COM is available from many BBSs in an .ARC file ; that starts with A86. ; In particular, A86.COM is available from the "AsmLang and CFS" BBS ; (Opus 1:143/37) 408-259-2223 in the file A86V319A.ZIP. ; Or HomeBase 408-988-4004 in the file A86V309A.ARC. ; Or PDSE 408-745-0880 in the file A86V314A.ARC. ; ; FASTPRIN installs a TSR which speeds up the printing done by the ; DOS PRINT command by calling INT 02FH with AX = 0080H whenever the ; system is idle. We know the system is idle whenever: ; ; 1. INT 016 is called with AH = 0 and there are no characters in ; the keyboard buffer. or ; 2. INT 015 is called with AH = 090. This will only happen on an AT. ; ; Termination codes: ; 0 - Successfully installed. ; 1 - Unable to determine if already installed, installed anyway. ; 2 - Already installed. ; ; If this program has been helpful to you, then please send a ; small gift to the author, Dennis Yelle, PO Box 62276, ; Sunnyvale, CA 94088. A gift of $10 is suggested. cr = 0d lf = 0a even old_int_15 dw 2 dup (?) old_int_16 dw 2 dup (?) get_vect macro push es #rx1l mov ax, 03500 + 0#x int 021 mov old_int_#x, bx mov old_int_#x[2], es #er pop es #em set_vect macro #rx1l mov dx, offset new_int_#x mov ax, 02500 + 0#x int 021 #er #em new_int_16: pushf sti test ah,ah jz wait_16 popf jmp cs: d old_int_16 wait_16_loop: mov ax, 0080 int 02f ; Do some printing. wait_16: mov ah, 1 int 016 ; Are any chars ready? jz wait_16_loop ; Jump if not. mov ah, 0 popf jmp cs: d old_int_16 new_int_15: pushf sti cmp ah, 090 je idle popf jmp cs: d old_int_15 idle: push ax mov ax, 0080 int 02f ; Do some printing. pop ax popf jmp cs:d old_int_15 end_of_resident: ;--------------------------------------------------------- install: mov ah, 040 mov bx, 1 ; Write to stdout mov dx, message0 mov cx, message0_len int 021 mov ah, 052 int 021 mov ax, es:[bx-2] mov dx, ds dec dx cld find: cmp ax, dx je install_it ja mem_bad mov es, ax mov si, 0100 mov di, 0110 mov cx, id_size/2 repe cmpsw je already_installed stc adc ax, es:[3] jnc find mem_bad: mov w installed_msg, mem_bad_msg inc b term_code install_it: get_vect 15, 16 set_vect 15, 16 mov ah, 049 mov es, [02c] int 021 ; Free the ENV segment. mov dx, installed_msg mov ah, 9 int 021 ; Print the INSTALLED message. mov ah, 031 mov al, term_code mov dx, (end_of_resident+15)/16 int 021 ; Terminate and stay resident. ;------------------------------------------------------------- already_installed: mov dx, ai_msg mov ah, 9 int 021 ; Print the message. mov ax, 04c02 int 021 installed: db 'FASTPRIN is now installed.', cr, lf, '$' mem_bad_msg: db 'FASTPRIN was unable to determine if it was previously installed, or not,' db cr, lf, db 'so it installed itself anyway.', cr, lf, '$' ai_msg: db 'FASTPRIN was already installed.', cr, lf, '$' installed_msg dw installed term_code db 0 ;THIS IS THE SOURCE CODE FOR THE EXEC FUNCTION ARTICLE ; ;THIS PROGRAM WITH FEW IF ANY SYNTAX CHANGES SHOULD BE COMPATIBLE WITH BOTH A86 ;AND MASM. ;IT IS NOT DESIGNED TO BE A STAND ALONE PROGRAM OR EVEN USER FRIENDLY BUT A ;BARE SHELL WITH WHICH TO PLAY AROUND WITH IN LEARNING TO USE THE DOS EXEC ;FUNCTION ;THE PROGRAM NAME SHOULD BE ENTERED BETWEEN THE QUOTE MARKS AT THE VARIABLE ;-PROGRAM- AND ANY OTHER COMMAND LINE ARGUMENTS SHOULD BE ENTERED BETWEEN THE ;QUOTE MARKS AT THE VARIABLE -COMMAND- BEFORE ASSEMBLY. MANY IMPROVEMENTS ARE ;OBVIOUS, SUCH AS BEING ABLE TO ENTER THE NAMES AND ARGUMENTS FROM THE CONSOLE ;BUT THESE ARE LEFT TO EXPERIMENTER. ;THE ONE SOPHISTICATION IF YOU COULD CALL IT THAT IS THAT THE ERROR LEVEL OF ;THE EXEC'D PROGRAM IS FETCHED, BUT NOTHING IS DONE WITH IT. CODE SEGMENT 'CODE' ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE org 0 PROG_ORG LABEL BYTE org 0100 ENTRY: JMP START db 32 DUP ('STACK ') ;THIS SETS ASIDE 256 BYTES- ;REQUIRED DOS STACK STAK LABEL BYTE ;THIS MARKS TOP OF STACK THIS_SEG DW 0 ;STORAGE FOR ADDRESS OF THIS SEGMENT CTL_LEN DW 0 ;THIS IS THE START OF THE ***** PROGRAM DB 'c:\dos\list.com',0 ;ASCIIZ STRING OF PROGRAM TO EXEC CMDL LABEL BYTE ;BEG ADDRESS OF COMMAND TAIL CMDLEN DB 0 ;OFFSET CR - OFFSET COMMAND COMMAND DB 'exec.asm' ;THE COMMAND TAIL ITSELF CR DB 0DH ;TERMINATING CARRIAGE RETURN PARMS LABEL BYTE ;ADDRESS OF PARAMETER TABLE ENV DW 0 ;POINTER TO ENVIRONMENT BLOCK ;0 DEFAULTS TO PARENT CMDLNLOW DW 0 ;OFFSET OF COMMAND TAIL CMDLNHI DW 0 ;SEGMENT OF COMMAND TAIL FCB11 DW 0 ;OFFSET OF FCB1 FCB12 DW 0 ;SEGMENT OF FCB1 FCB21 DW 0 ;OFFSETT OF FCB2 FCB22 DW 0 ;SEGMENT OF FCB2 MS1 DB 'DEALLOCATION ERROR',0DH,'$' ;MESSAGE #1 MS2 DB 'EXEC ERROR',0DH,'$' ;MESSAGE #2 START: CLI ;TURN OFF INTERRUPTS WHILE SWITCHING ;STACK FRAMES MOV SP,OFFSET STAK ;POINT STACK WHERE WE WANT IT STI ;INTS OK NOW MOV AX,DS ;BEING A COM PROGRAM DS CONTAINS THIS ;SEGMENT MOV THIS_SEG,AX ;PUT IT WHERE WE CAN GET IT EASILY RESIZE: MOV BX,(OFFSET PROG_END - OFFSET PROG_ORG) ;FIND OUT ;HOW BIG THE PROGRAM IS MOV CL,4 ;TURN THIS VALUE INTO PARAGRAPHS SHR BX,CL ; INC BX ;NEEDS 1 MORE TO CATCH OVERFLOW MOV AH,04AH ;ASK FOR DEALLOCATION OF ALL BUT THIS INT 021H JNC S1 ;IF CARRY BAD PROBLEM MOV AX,OFFSET MS1 ;POINT AX AT MESSAGE 1 JMP END_ERROR ;NOW LEAVE S1: ;AN ASSUMPTION IS MADE AT THIS POINT, THAT ;THERE IS ENOUGH MEMORY FOR THE PROGRAM TO BE ;EXEC'D TO OPERATE IN. IF NOT AN EXEC ERROR ;WILL BE RETURNED. MOV AX,OFFSET CR ;FIND LENGTH OF COMMAND TAIL SUB AX,OFFSET CMDL ;AND MOV CMDLEN,AL ;PUT IT IN CMDLEN MOV DX,OFFSET CMDL ;FILL IN THE COMMAND TAIL POINTER IN ;THE PARAMETER BLOCK MOV CMDLNLOW,DX MOV CMDLNHI,DS MOV DX,OFFSET PROGRAM ;POINT DX AT PROGRAM NAME MOV ES,THIS_SEG ;MAKE SURE ES IS POINTING AT THIS SEG MOV BX,OFFSET PARMS ;POINT BX AT PARAMETER BLOCK MOV AX,05CH ;THIS IS OFFSET OF PARENT FCB1 BUT ;COULD BE ANY INFO DESIRED TO PASS TO ;CHILD THAT WOULD BE LOOKED FOR AT PSP ;5CH MOV FCB11,AX ;PUT IT IN PARAMETER BLOCK MOV FCB12,ES ;DESIRED SEGMENT OF FCB1 DATA MOV AX,06CH ;FILL IN FCB2. ABOVE COMMENTS APPLY MOV FCB21,AX MOV FCB22,ES MOV AH,04BH ;THE EXEC FUNCTION MOV AL,0 ;LOAD AND EXECUTE PREX: INT 021H ;DOS JNC FINISHED ;ALL ACCORDING TO PLAN MOV AX,OFFSET MS2 ;SOMETHING WRONG IN EXEC JMP SHORT END_ERROR FINISHED: MOV AH,04DH ;TO REACH HERE THE CHILD MUST HAVE ;TERMINATED. THIS REQUESTS THE ;TERMINATION CODE INT 021H ;DOS TERMINATION CODE RETURNED IN AH ;CHILD'S AL PASSED THROUGH MOV AH,04CH ;TERMINATE THIS PROG INT 021H END_ERROR: MOV DX,AX ;SAVE THE MESSAGE ADDRESS TO DX MOV BX,1 ;DEVICE HANDLE IS CONSOLE MOV CX,20 ;20 BYTES IS ENOUGH FOR THE MESSAGE MOV AH,040H ;WRITE TO DEVICE (CONSOLE) INT 021H ;DOS MOV AH,04CH ;TERMINATE THIS PROGRAM INT 021H PROG_END LABEL BYTE CODE ENDS END ENTRY PAGE 60,132 TITLE EXMPLTSR.ASM - An example of a TSR program COMMENT~********************************************************************* * --++**> This file is Copyright 1989 by David O'Riva <**++-- * ***************************************************************************** * * * Written for the Microsoft Macro Assembler version 5.1, DOS v2.0 - 3.3 * * * * MUST BE CONVERTED INTO A .COM FILE BEFORE RUNNING!!! * * * * Anyone who wants to incorporate this code into their programs is * * welcome to, as long as they don't try to sell this file as it is or any * * unmodified piece of it, and leave this message in when distributing it. * * If you do not abide by the rules, poltergeists will invade your home * * and chain letters and junk mail will arrive by the ton. * * * * * * Short advertisement - use QEdit! * * Available on your local friendly Bulletin Board * * * ****************************************************************************~ .XLIST ; ; Macros used by this program ; ?PLevel = 0 PNPROC MACRO PNAME ;;declare near public procedure IF2 %OUT Routine: &PNAME ENDIF PUBLIC &PNAME &PNAME PROC NEAR ?PLevel = ?PLevel+1 ;;next level of nesting @@SAVE_NAME &PNAME,%?PLevel ENDM PFPROC MACRO PNAME ;;declare near public procedure IF2 %OUT Routine: &PNAME ENDIF PUBLIC &PNAME &PNAME PROC FAR ?PLevel = ?PLevel+1 ;;next level of nesting @@SAVE_NAME &PNAME,%?PLevel ENDM ENDPROC MACRO @@REC_NAME %?PLevel @@EP1 %@@TEMP ?PLevel = ?PLevel-1 ENDM @@SAVE_NAME MACRO PNAME,LVL ?PN&LVL EQU <&PNAME> ENDM @@REC_NAME MACRO LVL @@TEMP EQU ENDM @@EP1 MACRO PNAME &PNAME ENDP ENDM PUSHM MACRO LST IRP REG,<&LST&> PUSH REG ENDM ENDM POPM MACRO LST IRP REG,<&LST&> POP REG ENDM ENDM UPCASE MACRO REG LOCAL NOUP CMP REG,'a' JB NOUP CMP REG,'z' JA NOUP SUB REG,'a'-'A' NOUP: ENDM @CHANGE_VECT MACRO INUM,GARB,NEW,GARB2,SAVEAREA MOV AX,0 MOV ES,AX MOV AX,ES:[INUM*4] MOV DX,ES:[INUM*4+2] MOV WPTR CS:[SAVEAREA],AX MOV WPTR CS:[SAVEAREA+2],DX MOV AX,OFFSET CS:NEW CLI MOV ES:[INUM*4],AX MOV ES:[INUM*4+2],CS STI ENDM @RESTORE_VECT MACRO INUM,GARB,SAVEAREA MOV AX,WPTR CS:[SAVEAREA] MOV DX,WPTR CS:[SAVEAREA+2] CLI MOV DS:[INUM*4],AX MOV DS:[INUM*4+2],DX STI ENDM BPTR EQU WPTR EQU DPTR EQU CR EQU <13> LF EQU <10> JR EQU FALSE EQU <000H> TRUE EQU <0FFH> INT_CTRL EQU 020H ;Interrupt control port EOI EQU 020H ;Reset interrupt controller command KB_DATA EQU 060H ;Keyboard data port KB_CTRL EQU 061H ;Keyboard control port ; ;**************************************************************************** ; BIOSDATA SEGMENT AT 00040H ;---------------------------------------------------------------------------- ;Keyboard Data Area ;---------------------------------------------------------------------------- ORG 00017H KB_FLAG LABEL BYTE ;----- Shift flag equates within KB_FLAG INS_STATE EQU 80H ;INSERT state is active CAPS_STATE EQU 40H ;CAPS LOCK state toggled NUM_STATE EQU 20H ;NUM LOCK state toggled SCROLL_STATE EQU 10H ;SCROLL LOCK state toggled ALT_SHIFT EQU 08H ;ALT key depressed CTRL_SHIFT EQU 04H ;CTRL key depressed LEFT_SHIFT EQU 02H ;left SHIFT key depressed RIGHT_SHIFT EQU 01H ;right SHIFT key depressed ORG 00018H KB_FLAG_1 LABEL BYTE ;----- Shift flag equates within KB_FLAG_1 INS_SHIFT EQU 80H ;INSERT key depressed CAPS_SHIFT EQU 40H ;CAPS LOCK key depressed NUM_SHIFT EQU 20H ;NUM LOCK key depressed SCROLL_SHIFT EQU 10H ;SCROLL LOCK key depressed HOLD_STATE EQU 08H ;suspend key has been toggled ORG 00019H ALT_INPUT LABEL BYTE ;storage for alternate keypad entry ORG 0001AH BUFFER_HEAD LABEL WORD ;pointer to head of keyboard buffer ORG 0001CH BUFFER_TAIL LABEL WORD ;pointer to tail of keyboard buffer ORG 0001EH KB_BUFFER LABEL WORD ;keyboard buffer ORG 0003EH KB_BUFFER_END LABEL WORD ; ;----- HEAD = TAIL indicates that the buffer is empty NUM_KEY EQU 69 ;scan code for NUM LOCK SCROLL_KEY EQU 70 ;sc for SCROLL LOCK ALT_KEY EQU 56 ;sc for ALT key BIOSDATA ENDS .list .lall ; ;**************************************************************************** ; CODE SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE ORG 02CH ENVIRONMENTSEG LABEL WORD ORG 100H MAIN PROC FAR STACKTOP: ;allow the stack to overwrite the ;command line in the PSP ENTRY: JMP INSTALL ;============================================================================ ; ; MY LOCAL DATA ; MCBOVL STRUC ;definition of DOS's memory control ; ;block structure MCB_KIND DB ' ' MCB_PSP_ADDR DW 0 MCB_LENGTH DW 0 MCB_UNDEFINED DB 11 DUP(?) ; MCBOVL ENDS m@BLOCK EQU <'M'> ;possible entries in the MCB_TYPE field m@LAST EQU <'Z'> OLD9 DD ? ;old INT 09H vector OLD08 DD ? OLD28 DD ? OLD69 DD ? InDOS DD ? ;address of the InDOS flag CODSEG DW ? ;code segment AllowPop DB FALSE ;TRUE = the INT 09H server is allowed ; to try to run the TSR. TryPop DB FALSE ;TRUE = The INT 09H server couldn't ; run the TSR because DOS was ; processing a command at the ; time. ; InPop DB FALSE ;TRUE = the popped-up code is running ; at the moment. OLDSS DW ? ;storage for stack frame information OLDSP DW ? PopShifts DB LEFT_SHIFT+RIGHT_SHIFT ;shift state necessary for ; pop-up PopKey DB 14H ;key # to press to pop up ('T') ;============================================================================ PAGE ;**************************************************************************** ; START - main program loop ; ; ; ENTRY: from hot key ; ; EXIT: nothing ; ; DESTROYED: none ; ;---------------------------------------------------------------------------- PNPROC START ;------------------------------------------------------------------------------ ; set up my stack frame ;------------------------------------------------------------------------------ MOV CS:OLDSS,SS MOV CS:OLDSP,SP CLI MOV SS,CS:CODSEG MOV SP,OFFSET STACKTOP STI PUSH AX ;------------------------------------------------------------------------------ ; MAIN LOOP ;------------------------------------------------------------------------------ CALL BEEP GETAKEY: MOV AH,0 ;get a key INT 016H UPCASE AL CMP AL,'N' ;'N' for NUKE THE TSR! JNE NOT_NUKE CALL UNLOAD ;try to unload myself JNC LEAVE CALL BEEP ;string of beeps if I can't CALL BEEP CALL BEEP JMP GETAKEY NOT_NUKE: CMP AL,'Q' ;'Q' for quit TSR JE LEAVE ; CALL BEEP ;unrecognized key, complain JMP GETAKEY ;... and get another LEAVE: POP AX ;recover * ALL * modified registers ; ------- ;------------------------------------------------------------------------------ ; recover original stack frame ;------------------------------------------------------------------------------ CLI MOV SS,CS:OLDSS MOV SP,CS:OLDSP STI RET ENDPROC PAGE ;****************************************************************************** ; BEEP - Beeps the speaker ; ; ; ENTRY: nothing ; ; EXIT: nothing ; ; DESTROYED: none ; ;------------------------------------------------------------------------------ PNPROC BEEP PUSH AX PUSH CX MOV AL,10110110B OUT 043H,AL MOV AX,1000 OUT 042H,AL MOV AL,AH OUT 042H,AL IN AL,061H MOV AH,AL OR AL,3 OUT 061H,AL SUB CX,CX LOOP $ MOV AL,AH OUT 061H,AL POP CX POP AX RET ENDPROC ; PAGE ;;**************************************************************************** ;; REGISTERTOTEXT - Converts AL into ASCII hex digits in CS:[SI] ;; ;; For debugging - uncomment this routine if you need it. ;; ; ;; ENTRY: AL = register to translate ;; SI = place to put translated digits ;; ;; EXIT: AX = hex digits ;; ;; DESTROYED: AX ;; ;;---------------------------------------------------------------------------- ;ASSUME ds:NOTHING,es:NOTHING ;PNPROC REGISTERTOTEXT ;;---------------------------------------------------------------------------- ;; split AL into two nibbles ;;---------------------------------------------------------------------------- ; MOV AH,AL ; SHR AH,1 ; SHR AH,1 ; SHR AH,1 ; SHR AH,1 ; AND AL,0FH ;;---------------------------------------------------------------------------- ;; convert AL into a hex digit ;;---------------------------------------------------------------------------- ; ADD AL,'0' ;AL = actual digit ; CMP AL,'9' ; JBE R_1 ; ADD AL,'A'-'0'-10 ;;---------------------------------------------------------------------------- ;; convert AH into a hex digit ;;---------------------------------------------------------------------------- ;R_1: ADD AH,'0' ;AH = actual digit ; CMP AH,'9' ; JBE R_2 ; ADD AH,'A'-'0'-10 ;;---------------------------------------------------------------------------- ;; store hex number in [SI] ;;---------------------------------------------------------------------------- ;R_2: MOV CS:[SI],AH ; MOV CS:[SI+1],AL ; RET ;ENDPROC ; PAGE ;**************************************************************************** ; TRAPPER9 - Intercepts the incoming keyboard scan code ; ; Looks for the hot key in the incoming scan codes. If found, it ; check the InDOS flag to see if DOS is currently in the middle of something. ; If not, the TSR code (at START) is invoked. Otherwise, a flag is set and ; control is passed back to DOS. ; ; ENTRY: from IRQ 1, machine state is ??? ; ; EXIT: continues KB interrupt chain ; ; DESTROYED: ALL PRESERVED ; ;---------------------------------------------------------------------------- ASSUME ds:BIOSDATA,es:NOTHING PFPROC TRAPPER9 PUSHM ;save everthing I use MOV AX,SEG BIOSDATA ;DS-> BIOS's data seg MOV DS,AX ;---------------------------------------------------------------------------- ; are we in the correct shift state? ;---------------------------------------------------------------------------- MOV AL,KB_FLAG ;get current shift states AND AL,00FH ;clean up the byte CMP AL,CS:PopShifts ;is it the right shift? JNE T_chainon ;no, go to next handler IN AL,KB_DATA ;Poll keyboard controller MOV BH,AL ;save keypress AND AL,07FH ;strip off the MAKE/BREAK bit CMP AL,CS:PopKey ;is this our key? JNE T_ChainOn ;If it's not our code, ignore it... ;---------------------------------------------------------------------------- ; reset the keyboard controller ;---------------------------------------------------------------------------- IN AL,KB_CTRL ;get multi-purpose control byte MOV AH,AL ;save original value OR AL,080H ;set "character recieved" bit OUT KB_CTRL,AL ;send it MOV AL,AH ;get original value back OUT KB_CTRL,AL ;send it ;------------------------------------------------------------------------------ ; what do we do with this key? ;------------------------------------------------------------------------------ CMP CS:AllowPop,FALSE ;Are we allowed to pop up now? JE T_ResetLeave ;if not, exit here... TEST BH,080H ;was it the BREAK code? JZ T_WasMake ;no, go run the pop-up JMP T_ResetLeave ;otherwise, ignore this code T_WasMake: MOV CS:AllowPop,FALSE ;can't pop up again PUSHM ;check the InDOS flag... LES BX,CS:InDOS CMP BPTR ES:[BX],0 POPM JNZ T_InDOSnow ;if in DOS, don't invoke TSR MOV AL,EOI ;otherwise, set interrupts on... OUT INT_CTRL,AL MOV CS:InPop,TRUE ;...set a flag... STI CALL START ;...and run the TSR. CLI MOV CS:AllowPop,TRUE MOV CS:InPop,FALSE JMP T_Leave T_InDOSnow: MOV CS:TryPop,TRUE ;DOS call in progress, try later... ; T_ResetLeave: MOV AL,EOI ;enable the KB interrupt again OUT 020H,AL T_Leave: POPM ;throw away this key code IRET ;---------------------------------------------------------------------------- ; Continue down the KB handler chain... ;---------------------------------------------------------------------------- T_chainon: POPM JMP DWORD PTR CS:OLD9 ENDPROC PAGE ;****************************************************************************** ; TRAPPER08 - Intercepts the timer tick to try to pop up the TSR ; ; Upon being called, this routine determines whether: ; a) the TSR wants to pop up, and ; b) there is no DOS call in progress ; If these are BOTH true, then the TSR is invoked from here. ; ; NOTE: Technically, you are supposed to trap vector 1CH for timer ; ticks instead of INT 08, but some BIOSes do not issue an EOI for the timer ; until AFTER 1C is called, and you don't want to mess up the system clock... ; ; ENTRY: on timer tick ; ; EXIT: nothing ; ; DESTROYED: ALL PRESERVED ; ;------------------------------------------------------------------------------ PFPROC TRAPPER08 ASSUME ds:NOTHING,es:NOTHING PUSHF CALL DWORD PTR CS:[OLD08] CLI CMP CS:TryPop,FALSE ;Are we trying to pop up? JE C_OUT ;if not, leave now PUSHM ;Is a DOS call in progress? LES BX,CS:InDOS CMP BPTR ES:[BX],0 POPM JE C_Invoke ;if not, then fire it up... C_OUT: IRET C_Invoke: MOV CS:TryPop,FALSE ;set the right flags... MOV CS:InPop,TRUE STI CALL START ;run the TSR... MOV CS:InPop,FALSE ;set some more flags... MOV CS:AllowPop,TRUE ; IRET ;and leave. ENDPROC PAGE ;****************************************************************************** ; TRAPPER28 - Intercepts the DOS "console wait" loop ; ; This routine allows the TSR to be invoked during DOS console operations ; where the InDOS flag is set but it is actually safe to use any DOS function ; above 0CH. ; ; ENTRY: During DOS console operations ; ; EXIT: nothing ; ; DESTROYED: ALL PRESERVED ; ;------------------------------------------------------------------------------ ASSUME ds:NOTHING,es:NOTHING PFPROC TRAPPER28 CMP CS:TryPop,FALSE ;does the TSR want to pop up? JE C1_OUT ;if not, get out of here now MOV CS:TryPop,FALSE ;prevent re-entrant interrupts MOV CS:InPop,TRUE ;set the "in TSR" flag STI CALL START ;run it! CLI MOV CS:AllowPop,TRUE;finished, we can pop up again MOV CS:InPop,FALSE ;no longer running the TSR C1_OUT: JMP DWORD PTR CS:OLD28 ;continue down the console chain ENDPROC PAGE ;****************************************************************************** ; TRAPPER69 - Chains to INT 69H so we can determine if the TSR's already here ; ; This routine provides a method of checking to see whether a copy of ; this TSR already exists in memory. This entry could also be used to ; reconfigure the resident portion of the program (i.e. to reset a clock, ; to add a file to a print queue... etc.) ; ; ENTRY: AX = 0ABCDH ; ; EXIT: If the TSR is already resident: ; AX = 0DCBAH ; ; If the TSR is NOT resident: ; AX = 0ABCDH ; ; DESTROYED: AX if resident ; ;------------------------------------------------------------------------------ ASSUME ds:NOTHING,es:NOTHING PFPROC TRAPPER69 ; CMP AX,0ABCDH ;Is it the correct function? JE CK_OURS ;yes, respond to it JMP DWORD PTR CS:OLD69 CK_OURS: MOV AX,0DCBAH ;set return code IRET ENDPROC PAGE ;**************************************************************************** ; UNLOAD - Unhooks all vectors and exits ; ; This routine removes the TSR from memory, if possible. The sequence ; of events is: ; a) a check is made to be sure that we are the LAST program in memory ; b) all vectors are unhooked ; c) the environment segment that DOS gave me is deallocated ; d) the current code segment is deallocated ; e) control is returned to whatever program was running before ; ; ENTRY: nothing ; ; EXIT: nothing ; ; DESTROYED: this program, hopefully ; ;---------------------------------------------------------------------------- ASSUME ds:CODE,es:CODE PNPROC UNLOAD ;------------------------------------------------------------------------------ ; see if there are active memory control blocks after mine ;------------------------------------------------------------------------------ PUSH AX PUSH ES MOV AX,CS ;my MCB starts 1 seg before DEC AX ; my code seg MOV ES,AX ADD AX,ES:[0].MCB_LENGTH ;find out how long it is INC AX ;fiddle the value MOV ES,AX MOV AL,ES:[0].MCB_KIND ;what kind is the next MCB? CMP AL,m@LAST ;if it's not the last... POP ES POP AX JNZ BAD_UNLOAD ;...then leave now ;------------------------------------------------------------------------------ ; unhook all the vectors ;------------------------------------------------------------------------------ PUSH AX PUSH DX PUSH ES PUSH DS MOV AX,0 MOV DS,AX @RESTORE_VECT 009H FROM OLD9 ;Restore keyboard vector @RESTORE_VECT 008H FROM OLD08 ;Restore timer vector @RESTORE_VECT 028H FROM OLD28 ;Restore console vector @RESTORE_VECT 069H FROM OLD69 ;Restore my TSR vector ;------------------------------------------------------------------------------ ; deallocate everything ;------------------------------------------------------------------------------ MOV ES,CS:ENVIRONMENTSEG MOV AH,049H ;de-allocate the environment INT 021H ; block PUSH CS POP ES MOV AH,049H ;de-allocate the code block INT 021H POP DS POP ES POP DX POP AX CLC RET ;------------------------------------------------------------------------------ ;can't unload: memory control blocks after mine... ;------------------------------------------------------------------------------ BAD_UNLOAD: STC RET ENDPROC PAGE ;**************************************************************************** ; INSTALL - Installs traps, then runs the program. ; ; ENTRY: called on entry to the program ; ; EXIT: TSR's the main program and exits to DOS ; ; DESTROYED: ALL ; ;---------------------------------------------------------------------------- ASSUME ds:CODE,es:CODE PNPROC INSTALL PUSH CS PUSH CS POP DS POP ES MOV CS:CODSEG,CS ;------------------------------------------------------------------------------ ; see if this program is already in memory ;------------------------------------------------------------------------------ MOV AX,0ABCDH INT 069H CMP AX,0DCBAH JNE NOT_THERE JMP ALREADY_THERE ;---------------------------------------------------------------------------- ; install our keyboard hardware interrupt trap ;---------------------------------------------------------------------------- NOT_THERE: MOV AllowPop,FALSE MOV TryPop,FALSE MOV AX,03400H ;get the InDOS flag's address INT 21H MOV WPTR CS:InDOS,BX ;...and save it MOV WPTR CS:InDOS[2],ES @CHANGE_VECT 009H TO TRAPPER9 SAVEIN OLD9 @CHANGE_VECT 008H TO TRAPPER08 SAVEIN OLD08 @CHANGE_VECT 028H TO TRAPPER28 SAVEIN OLD28 @CHANGE_VECT 069H TO TRAPPER69 SAVEIN OLD69 ;---------------------------------------------------------------------------- ; print copyright notice ;---------------------------------------------------------------------------- MOV AH,9 MOV DX,OFFSET NOTICE INT 021H MOV CS:AllowPop,TRUE ;------------------------------------------------------------------------------ ; calculate total program size and leave ;------------------------------------------------------------------------------ TSR_OUT: MOV AX,03100H MOV DX,OFFSET INSTALL SHR DX,1 SHR DX,1 SHR DX,1 SHR DX,1 INC DX INT 021H ;------------------------------------------------------------------------------ ; ERROR: program is already resident! ;------------------------------------------------------------------------------ ALREADY_THERE: MOV AH,9 MOV DX,OFFSET ALREADY INT 021H MOV AX,04C01H INT 021H ENDPROC ;============================================================================== ; DATA THAT'S ONLY USED DURING INSTALLATION ; ; NOTICE LABEL BYTE DB CR,LF DB 'Example TSR v1.0 Sun 03-19-1989',CR,LF DB 'Copyright (C) 1988 ORivation',CR,LF DB CR,LF DB 'Press LSHIFT-RSHIFT-T to execute',CR,LF,'$' ALREADY LABEL BYTE DB CR,LF,'The Example TSR is already installed!',7,CR,LF,'$' MAIN ENDP ; ;**************************************************************************** ; CODE ENDS